package com.google.code.retrobox.editor.sprites;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.util.LinkedList;
import java.util.List;

import javax.swing.JPanel;

import com.google.code.retrobox.editor.commons.Tools;



public class SpriteRenderer extends JPanel {
	private static final int PIXEL_SIZE = 6;

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	public static final int 	SPRITES_X = 16,
								SPRITES_Y = 16;
	
	private SpriteEditor editor = null;
	private List<ActionListener> listeners = new LinkedList<ActionListener>();
	
	private PixelPanel[] pixels = new PixelPanel[SPRITES_X * SPRITES_Y];
	
	// Components
	private JPanel spritePanel = new JPanel(new GridLayout(SPRITES_Y, SPRITES_X));
	
	public SpriteRenderer(SpriteEditor ed) {
		this.editor = ed;
		
		init();
	}
	
	public void init() {
		setLayout(new BorderLayout());
		
		for (int i = 0;i < (SPRITES_X * SPRITES_Y);i++) {
			PixelPanel p = new PixelPanel(editor);
			p.setMinimumSize(new Dimension(PIXEL_SIZE, PIXEL_SIZE));
			p.setMaximumSize(new Dimension(PIXEL_SIZE, PIXEL_SIZE));
			p.setBg(Color.black);
			spritePanel.add(p);
			pixels[i] = p;
		}
		
		add(spritePanel, BorderLayout.CENTER);
	}
	
	public void setSprite(char[] pix) {
		int ind = 0;
		for (int i = 0;i < SPRITES_Y;i++) {
			for (int j = 0;j < (SPRITES_X);j++) {
				Color c;
				int d;
				if ((j % 2) != 0) {
					d = pix[(j + i * SPRITES_Y) / 2] & 0x000F;
				} else {
					d = pix[(j + i * SPRITES_Y) / 2] >>> 4;
				}
				
				c = Tools.getColor(d);
				pixels[ind].setBg(c);
				
				ind++;
			}
		}
	}

	public char[] getSprite() {
		char[] pix = new char[(SPRITES_X * SPRITES_Y) / 2];

		int ind = 0;
		for (PixelPanel element : pixels) {
			if (ind % 2 == 0) {
				pix[ind / 2] = (char) ((Tools.getColorIndex(element
						.getBackground()) << 4));
			} else {
				pix[ind / 2] = (char) (pix[ind / 2] | Tools
						.getColorIndex(element.getBackground()));
			}
			ind++;
		}

		return pix;
	}
	
	public char[] getSpriteByteArray() {
		char[] pix = new char[(SPRITES_X * SPRITES_Y)];

		int ind = 0;
		for (PixelPanel element : pixels) {
				pix[ind] = (char) (Tools.getColorIndex(element
						.getBackground()));
			ind++;
		}
		return pix;
	}
	
	public BufferedImage getBufferedImage(int scale){
		BufferedImage bufferedImage = new BufferedImage(SPRITES_X * scale, SPRITES_Y * scale, BufferedImage.TYPE_INT_RGB );
		bufferedImage.createGraphics();
		Graphics2D g = (Graphics2D)bufferedImage.getGraphics();
		int ind = 0;
		char[] pix = this.getSprite();
		for (int i = 0;i < SPRITES_X;i++) {
			for (int j = 0;j < SPRITES_Y;j++) {
				Color c;
				int d;
				if ((ind % 2) != 0) {
					d = pix[ind / 2] & 0x000F;
				} else {
					d = pix[ind / 2] >>> 4;
				}
				c = Tools.getColor(d);
				g.setColor(c);
				g.fillRect(j*scale, i*scale, 1*scale, 1*scale);
				ind++;
			}
		}
		return bufferedImage;
	}
	
	public void flipSpriteX(){
		final int BPC = 8;
		char[] buffer = new char[this.getSprite().length];
		int length = this.getSprite().length / BPC;
		for(int t=0;t < length;t++){
			buffer[t*BPC] =  swapNibble(this.getSprite()[t*BPC+7]);
			buffer[t*BPC+1] =  swapNibble(this.getSprite()[t*BPC+6]);
			buffer[t*BPC+2] =  swapNibble(this.getSprite()[t*BPC+5]);
			buffer[t*BPC+3] =  swapNibble(this.getSprite()[t*BPC+4]);
			buffer[t*BPC+4] =  swapNibble(this.getSprite()[t*BPC+3]);
			buffer[t*BPC+5] =  swapNibble(this.getSprite()[t*BPC+2]);
			buffer[t*BPC+6] =  swapNibble(this.getSprite()[t*BPC+1]);
			buffer[t*BPC+7] =  swapNibble(this.getSprite()[t*BPC]);			
		}
		this.setSprite(buffer);
	}
	
	private char swapNibble(char value){
		return (char) (((value & 0x0F) << 4) | (value >> 4));
	}
	
	public void flipSpriteY(){
		final int BPC = 8;
		char[] buffer = new char[this.getSprite().length];
		int length = this.getSprite().length / BPC;
		for(int t=0;t < length;t++){
			buffer[t*BPC] =  this.getSprite()[(16-1-t)*BPC];
			buffer[t*BPC+1] =  this.getSprite()[(16-1-t)*BPC+1];
			buffer[t*BPC+2] =  this.getSprite()[(16-1-t)*BPC+2];
			buffer[t*BPC+3] =  this.getSprite()[(16-1-t)*BPC+3];
			buffer[t*BPC+4] =  this.getSprite()[(16-1-t)*BPC+4];
			buffer[t*BPC+5] =  this.getSprite()[(16-1-t)*BPC+5];
			buffer[t*BPC+6] =  this.getSprite()[(16-1-t)*BPC+6];
			buffer[t*BPC+7] =  this.getSprite()[(16-1-t)*BPC+7];
		}
		this.setSprite(buffer);
	}


	class PixelPanel extends JPanel {
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		private Boolean mouseInside = false;
		//private PixelPanel self = this;
		private SpriteEditor spriteEditor = null;
		
		private Color	hoverColor = Color.black, 
						bgColor = Color.black;
		
		public PixelPanel(SpriteEditor e) {
			super();
			this.spriteEditor = e;
			
			addMouseListener(new java.awt.event.MouseListener() {
				public void mouseEntered(java.awt.event.MouseEvent evt) {
					mouseInside = true;
					
					if (spriteEditor != null) {
						hoverColor = spriteEditor.getCurrentColor();
						
						if (evt.isShiftDown()) {
							bgColor = hoverColor;
						}
						
						setBackground(hoverColor);
						repaint();
					}
				}

				public void mouseExited(java.awt.event.MouseEvent evt) {
					mouseInside = false;
					
					if (spriteEditor != null) {
						if (evt.isShiftDown()) {
							bgColor = hoverColor;
						}
						
						if (bgColor.getRGB()  != getBackground().getRGB()) {
							setBackground(bgColor);
							repaint();
						}
					}
				}


				public void mousePressed(MouseEvent arg0) {
					if (arg0.isPopupTrigger() && spriteEditor != null) {
						spriteEditor.setCurrentColor(bgColor);
					}
				}

				public void mouseReleased(MouseEvent arg0) {
					if (spriteEditor != null && arg0.getButton() == MouseEvent.BUTTON1 && mouseInside) {
						bgColor = hoverColor;
						
						if (bgColor.getRGB()  != getBackground().getRGB()) {
							setBackground(bgColor);
							repaint();
						}
					}
					
					if (arg0.isPopupTrigger() && spriteEditor != null) {
						spriteEditor.setCurrentColor(bgColor);
					}
					
				}

				public void mouseClicked(MouseEvent arg0) {
					
				}

			});
		}
		
		/**
		 * Use setBg to set the background color when tileEditor is not null.
		 */
		public void setBg(Color c) {
			bgColor = c;
			setBackground(c);
		}
		
		public boolean isMouseInside() {
			return mouseInside;
		}
	}
	
}
